package jacky.lanlan.song.aop;

import jacky.lanlan.song.aop.advice.*;

import java.util.ArrayList;
import java.util.List;

import org.aopalliance.aop.Advice;

/**
 * Advice链，每个被拦截的方法都对应一个Advice链，由这些Advice来实现AOP
 * 
 * @author Jacky.Song
 */
class AdviceChain{
	
	/*
	 * Chain里面的 Advice 都是Class，而不是具体的对象，这样，就形成了Lazy Loading，使用Chain的
	 * 类可以在需要的时候，再通过反射创建具体的 Advice 对象。
	 */
	
	private List<Class<? extends MethodBeforeAdvice>> methodBeforeAdvices=
		new ArrayList<Class<? extends MethodBeforeAdvice>> ();
	
	private List<Class<? extends AfterReturningAdvice>> afterReturningAdvices=
		new ArrayList<Class<? extends AfterReturningAdvice>> ();
	
	private List<Class<? extends ThrowsAdvice>> throwsAdvices=new ArrayList<Class<? extends ThrowsAdvice>> ();
	
	private List<Class<? extends AfterAdvice>> afterAdvices=new ArrayList<Class<? extends AfterAdvice>> ();

	private Class<? extends AroundAdvice> aroundAdvice;
	
	/**
	 * 由给定的拦截器列表创建拦截器链。
	 */
	public AdviceChain(List<Class<? extends Advice>> chain) {
		separateAdvice(chain);
	}
	
	public List<Class<? extends AfterAdvice>> getAfterAdvices() {
		return this.afterAdvices;
	}

	public List<Class<? extends AfterReturningAdvice>> getAfterReturningAdvices() {
		return this.afterReturningAdvices;
	}

	public List<Class<? extends MethodBeforeAdvice>> getMethodBeforeAdvices() {
		return this.methodBeforeAdvices;
	}

	public List<Class<? extends ThrowsAdvice>> getThrowsAdvices() {
		return this.throwsAdvices;
	}

	public Class<? extends AroundAdvice> getAroundAdvice() {
		return this.aroundAdvice;
	}
	
	/**
	 * 根据拦截器类型分离拦截器。
	 */
	@SuppressWarnings("unchecked")
	private void separateAdvice(List<Class<? extends Advice>> chain) {
		for(Class<? extends Advice> adviceClass:chain) {
			if(MethodBeforeAdvice.class.isAssignableFrom(adviceClass)) {
				methodBeforeAdvices.add((Class) adviceClass);
			}
			if(AfterReturningAdvice.class.isAssignableFrom(adviceClass)) {
				afterReturningAdvices.add((Class) adviceClass);
			}
			if(ThrowsAdvice.class.isAssignableFrom(adviceClass)) {
				throwsAdvices.add((Class) adviceClass);
			}
			if(AfterAdvice.class.isAssignableFrom(adviceClass)) {
				afterAdvices.add((Class) adviceClass);
			}
			if(AroundAdvice.class.isAssignableFrom(adviceClass)) {
				aroundAdvice=(Class<? extends AroundAdvice>) adviceClass;
			}
		}
	}
	
}